package animation;

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.util.ArrayList;

import javax.swing.ImageIcon;

import elements.GameCharacter;
import elements.GameElement;
import elements.GameElementFactory;

public class CopyOfAnimator extends Thread {
	public static final GameElementFactory factory = new GameElementFactory();

	private boolean backgroundPaint = false, foregroundPaint = false;
	private boolean bounce = false;
	private boolean freeze = false;
	private int milliRefresh = 20;
	private ArrayList<GameElement> elements;
	private Check check = null;
	private Container comp;
	private Graphics graphics;
	private Image image;
	private Color background = Color.WHITE;
	
	private ImageIcon rightorwrong = null;
	private int imX = 0, imY = 0;

	public CopyOfAnimator(Container comp) {
		this.elements = new ArrayList<GameElement>();
		this.comp = comp;
	}

	public CopyOfAnimator(Container comp, GameElement elements[]) {
		this.elements = new ArrayList<GameElement>(elements.length + 5);
		for(int i = 0; i<elements.length; i++) {
			this.elements.add(elements[i]);
		}

		this.comp = comp;
	}

	/**
	 * Adds the element to the list of elements.
	 * 
	 * @param el the element which should be added
	 */
	public void add(GameElement el) {
		elements.add(el);
	}

	public void update() {
		// If the component isn't visible, nothing will be painted.
		if(GraphicsEnvironment.isHeadless()) return;
		
		//Initialise Double-Buffer
		if (image == null || image.getHeight(null) != comp.getHeight() || image.getWidth(null) != comp.getWidth()) {
			image = comp.createImage(comp.getParent().getWidth(), comp.getParent().getHeight());
			graphics = image.getGraphics();
		}
		//Delete background
		graphics.setColor(background);
		graphics.fillRect(0, 0, comp.getWidth(), comp.getHeight());

		//Background Painting
		if(backgroundPaint) {
			graphics.setColor(comp.getForeground());
			comp.paint(graphics);
		}

		//Draw foreground
		graphics.setColor(comp.getForeground());
		for(int i = 0; i<elements.size(); i++) {
			elements.get(i).paint(graphics);
		}

		//Foreground Painting
		if(foregroundPaint) {
			graphics.setColor(comp.getForeground());
			comp.paint(graphics);
		}
		
		//Right or Wrong
		if(rightorwrong != null) 
			rightorwrong.paintIcon(comp, 
					graphics, 
					imX - rightorwrong.getIconWidth()/2, 
					imY - rightorwrong.getIconHeight()/2);
		

		//Paint everything
		comp.getGraphics().drawImage(image, 0, 0, null);

	}

	/**
	 * Starts the Animation.
	 */
	@Override
	public void run() {
		//Checks if the Thread got interrupted
		while(!this.isInterrupted()) {

			nextFrame();

			try {
				//Wait for the next refresh
				do {
					sleep(milliRefresh);
				} while(freeze);
			} catch(InterruptedException e) {
				//Thread got interrupted. isInterrupted() will be true now
				this.interrupt();
			}
		}
	}

	/**
	 * Freezes the Animator.<br>
	 * If the Animator is already freezed, nothing happens.
	 */
	public void freeze() {
		freeze = true;
	}

	/**
	 * Unfreezes the Animator.<br>
	 * If the Animator is running, nothing happens.
	 */
	public void unfreeze() {
		freeze = false;
	}
	/**
	 * Will reset the given elements.
	 */
	public void reset() {
		for(int i = 0; i<elements.size(); i++) {
			elements.get(i).reset();
		}
	}

	/**
	 * All elements in the Animator are removed.
	 */
	public void clear() {
		elements.clear();
	}

	/**
	 * Sets the interval (in milliseconds) in which the elements
	 * will be refreshed. Needs to be >= 10.
	 */
	public void setRefreshTime(int refresh) {
		if(refresh < 10) return;

		milliRefresh = refresh;
	}

	/**
	 * Activates the repaint option, so the Animator
	 * calls the paint-method from the given component.<br>
	 * Will paint it behind everything else.
	 * 
	 * Default is false.<br>
	 * ForegroundPainting will be false if paint is true.
	 * @param paint
	 */
	public void setBackgroundPainting(boolean paint) {
		if(foregroundPaint && paint) foregroundPaint = false;
		this.backgroundPaint = paint;
	}

	/**
	 * Activates the repaint option, so the Animator
	 * calls the paint-method from the given component.<br>
	 * Will paint it in front of everything else.
	 * 
	 * Default is false.<br>
	 * BackgroundPainting will be false if paint is true.
	 * 
	 * @param paint
	 */
	public void setForegroundPainting(boolean paint) {
		if(backgroundPaint && paint) backgroundPaint = false;
		this.foregroundPaint = paint;
	}

	/**
	 * Activates a bouncing effect.<br>
	 * The elements will bounce of the window borders
	 * if bounce is true.
	 * 
	 * @param bounce true = bouncing <br>
	 * 		  		 false = no bouncing
	 */
	public void setBouncing(boolean bounce) {
		this.bounce = bounce;
	}

	/**
	 * Will set a Check function for the animator.<br>
	 * The check-methode will be called after every single update
	 * @param check The Check Function
	 */
	public void addCheck(Check check) {
		this.check = check;
	}
	
	public void removeElem(GameCharacter gc) {
		elements.remove(gc);

	}

	public void nextFrame() {
		//if(isAlive()) return;

		//Refreshs the elements
		for(int i = 0; i<elements.size(); i++) {
			GameElement ele = this.elements.get(i);			
			ele.update();
			
			// Fadeout
			if(ele.shouldFadeOut() && ele.getTransparency() > GameElement.NOT_VISIBLE) {
				if(ele.getTransparency() <= 0.05f) ele.setTransparency(GameElement.NOT_VISIBLE);
				else ele.setTransparency(ele.getTransparency() - 0.05f);
			} 

			if(bounce) {
				//FIXME why does bounce not scale correctly?
				if(ele.getMovedX() <= 0) {
					ele.bounce(true, ele.getMovedX()-1);
				} else if((ele.getMovedX()+ele.getScaledWidth()) >= comp.getWidth()) {
					ele.bounce(true, (ele.getMovedX() + ele.getScaledWidth() + 1) - comp.getWidth());
				} if(ele.getMovedY() <= 0) {
					ele.bounce(false, ele.getMovedY()-1);
				} if((ele.getMovedY()+ele.getScaledHeight()) >= comp.getHeight()) {
					ele.bounce(false, (ele.getMovedY() + ele.getScaledHeight() + 1) - comp.getHeight());
				}

			}
		}
		update();
		
		if(check != null) check.check();
	}

	public int size() {
		return elements.size();
	}

	public GameElement[] getElements() {
		GameElement ele[] = new GameElement[size()];
		
		for(int i = 0; i<ele.length; i++) {
			ele[i] = elements.get(i);
		}
		
		return ele;
	}
	
	/**
	 * Sets the default background color for the Animator
	 * @param c the Color
	 */
	public void setBackgroundColor(Color c) {
		background = c;
	}

	public void setRightOrWrong(ImageIcon img, int x, int y) {
		this.rightorwrong = img;
		imX = x; imY = y;
	}
}
